#include "script/glue/LuaGlue.h"
#include "core/Object.h"

namespace LuaGlue
{

	int Object::create(lua_State *L)
	{
		::Object *o = new ::Object();

		//!TODO: copy constructor and such...
		if(lua_istable(L, 1))
			{
				//const char *key = luaL_checkstring(L, 2);
			}

		Wrapper<Object>::Instance(L, (void*)o);

		return 1;
	}

	//!TODO: gc
	int Object::gc(lua_State *L)
	{
		return 0;
	}

	int Object::getName(lua_State *L)
	{
		::Object *o = unwrap< ::Object>(L, 1);
		lua_pushstring(L, o->getName().c_str());
		return 1;
	}

	int Object::tostring (lua_State *L)
	{
		char buff[32];

		switch(lua_gettop(L))
			{
			case 0:
				lua_pushstring(L, className);
				break;
			case 1:
				sprintf(buff, "%s", unwrap< ::Object >(L, 1)->getName().c_str());
				lua_pushfstring(L, "%s (\"%s\")", className, buff);
				break;
			default:
				return 0;
				break;
			}
		return 1;
	}

	int Object::toTable(lua_State *L)
	{
		::Object *o = unwrap< ::Object>(L, 1);

		lua_newtable(L);

		PropertyMapIterator it = o->properties.begin();
		for(; it!=o->properties.end(); ++it)
			{
				pair<std::string, IProperty*> pair = *it;
				IProperty* p = pair.second;
				lua_pushstring(L, pair.first.c_str()); // key
				//!TODO:: userdata
				lua_pushstring(L, p->getTypeName().c_str()); // value
				lua_rawset(L, -3);
			}

		return 1;
	}

	int Object::get( lua_State *L )
	{
		::Object *o = unwrap< ::Object>(L, 1);

		const char *key = luaL_checkstring(L, 2);

		if(Wrapper<Object>::Method(L, key)) return 1;

		if(o->hasProperty(key))
			{
				IProperty* p = (*o)[key];
				void* ptr = p->_getValuePtr();
				const std::string& typeName = p->getTypeName();
				switch(TypeInfo::getType(typeName))
					{
						// POD
					case BOOL:   lua_pushboolean(L, prop_cast_val<bool>(p)); break;
					case INT:    lua_pushinteger(L, prop_cast_val<int>(p)); break;
					case FLOAT:  lua_pushnumber	(L, prop_cast_val<float>(p)); break;
					case STRING: lua_pushinteger(L, prop_cast_val<int>(p)); break;

					case OBJECT:
						Wrapper<Object>::Instance(L, ptr);
						break;

					case FUNCTION:
						//!TODO
						lua_pushnil	(L);
						{
							::Function f = prop_cast_val< ::Function >(p);
							lua_CFunction lua_f;

							std::cout << key << "," << f << std::endl;

							LuaFunctionHelper* ptr = new LuaFunctionHelper(f);
							lua_pushinteger(L, *reinterpret_cast<int*>(ptr));
							lua_pushcclosure(L, &LuaFunctionHelper::callback, 1);

//						lua_pushcclosure(L, lua_f, 0);
						}
						break;

						// OGRE
					case VECTOR3:
						Wrapper<Vector3>::Instance(L, ptr);
						break;
					case QUATERNION:
						Wrapper<Quaternion>::Instance(L, ptr);
						break;

						// Bullet
					case RIGIDBODY:
						Wrapper<RigidBody>::Instance(L, ptr);
						break;

					case NIL:
						if(!ptr)
							lua_pushnil	(L);
						else
							lua_pushlightuserdata(L, ptr);
						break;

					case UNKNOWN:
					default:
					{
						cout << key << "[" << typeName << "]" << endl;

						lua_newtable(L);

						luaL_getmetatable(L, typeName.c_str());
						lua_setmetatable(L, -2);

						lua_pushlightuserdata(L, ptr);
						lua_setfield(L, -2, "__pointer");

						luaL_getmetatable(L, typeName.c_str());
						lua_setfield(L, -2, "__metatable");

						luaL_getmetatable(L, typeName.c_str());
						lua_setfield(L, -2, "__index");
					}
					break;
					}
				return 1;
			}
		else
			{
				lua_pop(L, 2);
				lua_getfield (L, 1, key);
//				lua_gettable(L, 1);
			}
		return 1;
	}

	int Object::set( lua_State *L )
	{
		::Object *o = unwrap< ::Object>(L, 1);

		const char *key = luaL_checkstring(L, 2);

		if(o->hasProperty(key))
			{
				IProperty* p = (*o)[key];

				const char* typeName = p->getTypeName().c_str();
				switch(TypeInfo::getType(typeName))
					{
					case BOOL:
						prop_cast<bool>(p)->Set(lua_toboolean(L, 3));
						break;
					case INT:
						break;
					case FLOAT:
						prop_cast<float>(p)->Set(luaL_checknumber(L, 3));
						break;
					case STRING:
						prop_cast<std::string>(p)->Set(luaL_checkstring(L, 3));
						break;

					case OBJECT:
						break;
					case FUNCTION:
						break;

					case VECTOR3:
						prop_cast<Ogre::Vector3>(p)->Set(*unwrap<Ogre::Vector3>(L, 3));
						break;
					case QUATERNION:
						prop_cast<Ogre::Quaternion>(p)->Set(*unwrap<Ogre::Quaternion>(L, 3));
						break;

					case UNKNOWN:
					default:
						break;
					}
			}
		else
			{
//				lua_pushvalue(L, 3);
//				lua_setfield(L, 1, key);
				lua_rawset(L, 1);
			}
		return 1;
	}

	int Object::getXmlString(lua_State* L)
	{
		::Object *o = unwrap< ::Object>(L, 1);
		lua_pushstring(L, o->getXmlString().c_str());
		return 1;
	}
}

DECLARE(Object)
META(Object)
{"__tostring", LuaGlue::Object::tostring},
{"__index",    LuaGlue::Object::get},
{"__newindex", LuaGlue::Object::set},
{"__gc", 	   LuaGlue::Object::gc},
METHODS(Object)
method(LuaGlue::Object, getName),
method(LuaGlue::Object, getXmlString),
method(LuaGlue::Object, toTable),
END
